package com.nexgo.payment.trans.setting.terminalkey.manualtmk;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.text.TextUtils;

import com.nexgo.common.ByteUtils;
import com.nexgo.common.Des;
import com.nexgo.oaf.apiv3.SdkResult;
import com.nexgo.oaf.apiv3.card.cpu.APDUEntity;
import com.nexgo.oaf.apiv3.card.cpu.CPUCardHandler;
import com.nexgo.oaf.apiv3.device.pinpad.AlgorithmModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.PinPad;
import com.nexgo.oaf.apiv3.device.reader.CardInfoEntity;
import com.nexgo.oaf.apiv3.device.reader.CardReader;
import com.nexgo.oaf.apiv3.device.reader.CardSlotTypeEnum;
import com.nexgo.oaf.apiv3.device.reader.OnCardInfoListener;
import com.nexgo.payment.R;
import com.nexgo.payment.aop.annotation.RunOnMainThread;
import com.nexgo.payment.api.ApiDeviceEngine;
import com.nexgo.payment.constrant.PreferencesConstants;
import com.nexgo.payment.databases.DataSource;
import com.nexgo.payment.util.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.HashSet;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * 手输密文TMK
 *
 * @author 谭忠扬-YuriTam
 * @time 2017年08月30日
 */
public class ManualTmkPresenter implements ManualTmkContract.Presenter {
    private Logger mLog = LoggerFactory.getLogger(ManualTmkPresenter.class.getSimpleName());

    private final Context mContext;
    private final ManualTmkContract.View mView;
    private final DataSource mRepository;
    private PinPad mPinPad;
    private CardReader mCardReader;
    private CPUCardHandler mCardHandler;
    private String mKey;
    private String mPassword;
    private byte[] mVerify = null;
    private int algorithmType;

    public ManualTmkPresenter(@NonNull Context context, @NonNull ManualTmkContract.View view, @NonNull DataSource repository) {
        mContext = context.getApplicationContext();
        mView = checkNotNull(view);
        mRepository = checkNotNull(repository);
        mView.setPresenter(this);
        mPinPad = ApiDeviceEngine.getInstance().getPinPad();
        mCardReader = ApiDeviceEngine.getInstance().getCardReader();
        mCardHandler = ApiDeviceEngine.getInstance().getCPUCardHandler(CardSlotTypeEnum.ICC1);
        algorithmType = Integer.parseInt(StringUtils.firstNonEmpty(mRepository.getParamValue(PreferencesConstants.MASTER_KEY_ALGORITHM), "0"));
        if (algorithmType == AlgorithmType.SM4.getCode()) {
            mPinPad.setAlgorithmMode(AlgorithmModeEnum.SM4);
        }
    }

    @Override
    public void start() {

    }

    @Override
    public void pause() {

    }

    @Override
    public void destroy() {

    }

    @Override
    public void confirm(String key, String password, String verify) {
        if (TextUtils.isEmpty(key) || TextUtils.isEmpty(password) || TextUtils.isEmpty(verify)) {
            postMainThread(() -> mView.showInputEmpty());
            return;
        }
        if (key.length() % 8 != 0) {
            postMainThread(() -> mView.showErrMsg(mContext.getString(R.string.pls_input_correct_key_num)));
            return;
        }
        if (password.length() < 6) {
            postMainThread(() -> mView.showErrMsg(mContext.getString(R.string.pls_input_6_12_length_password)));
            return;
        }
        if (password.length() % 2 != 0) {
            password = password + "F";
        }
        this.mKey = key;
        this.mPassword = password;
        this.mVerify = ByteUtils.hexString2ByteArray(verify);
        postMainThread(() -> mView.showMsg(mContext.getString(R.string.pls_insert_decode_card)));
        try {
            HashSet<CardSlotTypeEnum> slotTypes = new HashSet<>();
            slotTypes.add(CardSlotTypeEnum.ICC1);
            mCardReader.searchCard(slotTypes, 60, mCardInfoListener);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取维护卡信息回调
     */
    private OnCardInfoListener mCardInfoListener = new OnCardInfoListener() {

        @Override
        public void onCardInfo(int retCode, CardInfoEntity cardInfo) {
            if (retCode != SdkResult.Success) {
                postMainThread(() -> mView.showErrMsg(mContext.getString(R.string.read_card_fail)));
                postMainThread(() -> mView.onDismiss());
                return;
            }
            postMainThread(() -> mView.showMsg(mContext.getString(R.string.downing_key_and_not_move_card)));
            byte[] atr = new byte[128];
            mCardHandler.powerOn(atr);
            //验证密码 0x00,0x20,0x00,0x81
            APDUEntity apduEntity = new APDUEntity();
            apduEntity.setCla((byte) 0x00);
            apduEntity.setIns((byte) 0x20);
            apduEntity.setP1((byte) 0x00);
            apduEntity.setP2((byte) 0x81);
            byte[] arr = new byte[256];
            Arrays.fill(arr, (byte) 0xFF);
            byte[] dat = ByteUtils.hexString2ByteArray(mPassword);
            System.arraycopy(dat, 0, arr, 0, dat.length);
            apduEntity.setDataIn(arr);
            apduEntity.setLc(8);
            apduEntity.setLe(0);
            apduEntity.setSwa((byte) 0x00);
            apduEntity.setSwb((byte) 0x00);
            int result = mCardHandler.exchangeAPDUCmd(apduEntity);
            if (result != SdkResult.Success || !isCmdSuc(apduEntity)) {
                postMainThread(() -> mView.showErrMsg(mContext.getString(R.string.password_verify_fail)));
                postMainThread(() -> mView.onDismiss());
                byte a = apduEntity.getSwa();
                byte b = apduEntity.getSwb();
                mLog.debug("return:{},{}", a, b);
                return;
            }
            //解密 0x80, 0xF8, 0x01, 0x02, 0x10, _in, 0x10
            apduEntity = new APDUEntity();
            apduEntity.setCla((byte) 0x80);
            apduEntity.setIns((byte) 0xF8);
            apduEntity.setP1((byte) 0x01);
            apduEntity.setP2((byte) 0x02);
            arr = new byte[256];
            Arrays.fill(arr, (byte) 0xFF);
            dat = ByteUtils.hexString2ByteArray(mKey);
            System.arraycopy(dat, 0, arr, 0, dat.length);
            apduEntity.setDataIn(arr);
            apduEntity.setLc(dat.length);
            apduEntity.setLe(dat.length);
            apduEntity.setSwa((byte) 0x00);
            apduEntity.setSwb((byte) 0x00);
            result = mCardHandler.exchangeAPDUCmd(apduEntity);
            if (result != SdkResult.Success || !isCmdSuc(apduEntity)) {
                postMainThread(() -> mView.showErrMsg(mContext.getString(R.string.cipher_decode_fail)));
                postMainThread(() -> mView.onDismiss());
                byte a = apduEntity.getSwa();
                byte b = apduEntity.getSwb();
                mLog.debug("return:{},{}", a, b);
                return;
            }
            byte[] key = new byte[16];
            byte[] temp = new byte[16];
            Arrays.fill(temp, (byte) 0x00);
            mLog.debug("密钥：{}", ByteUtils.byteArray2HexString(apduEntity.getDataOut()));
            System.arraycopy(apduEntity.getDataOut(), 0, key, 0, 16);
            byte[] curKvl = Des.trides_crypt(key, temp);
            if (!Arrays.equals(mVerify, Arrays.copyOfRange(curKvl, 0, 4))) {
                postMainThread(() -> mView.showErrMsg(mContext.getString(R.string.verify_fail)));
                postMainThread(() -> mView.onDismiss());
                return;
            }
            if (algorithmType == AlgorithmType.TDES.getCode()) {
                key = new byte[16];
                System.arraycopy(apduEntity.getDataOut(), 0, key, 0, 16);
            } else {
                key = new byte[8];
                System.arraycopy(apduEntity.getDataOut(), 0, key, 0, 8);
            }
            //密钥索引
            int index = Integer.parseInt(StringUtils.firstNonEmpty(mRepository.getParamValue(PreferencesConstants.MASTER_KEY_INDEX), "0"));
            result = mPinPad.writeMKey(index, key, key.length);
            mLog.debug("loadMK result={},index={}", result, index);
            postMainThread(() -> mView.onDismiss());
            if (result != SdkResult.Success) {
                postMainThread(() -> mView.showFail());
                return;
            }
            postMainThread(() -> mView.showSuccessful());
        }

        @Override
        public void onSwipeIncorrect() {

        }

        @Override
        public void onMultipleCards() {

        }
    };

    /**
     * 判断命令
     *
     * @param apduEntity
     * @return
     */
    private boolean isCmdSuc(APDUEntity apduEntity) {
        if (apduEntity.getSwa() == (byte) 0x90 && apduEntity.getSwb() == (byte) 0x00) {
            return true;
        }
        return false;
    }

    @RunOnMainThread
    private void postMainThread(Runnable runnable) {
        runnable.run();
    }

    enum AlgorithmType {

        EDS(0),
        TDES(1),
        SM4(2);

        private int code;

        AlgorithmType(int code) {
            this.code = code;
        }

        public int getCode() {
            return code;
        }
    }
}
